#include <ssh/server.h>
#include <ssh/client.h>
#include <libssh/server.h>
#include <fort.h>
#include <libssh/callbacks.h>
#include <sys/stat.h>
#include <thread>
using namespace fort;
// 服务端认证通道相关回调
int SSHServer::auth_pass(ssh_session session, const char *user, const char *password, void *userdata)
{
    struct SSHFort *sdata = (SSHFort *)userdata;
    int flag = sdata->flag;
    if (strcmp("root", user) == 0 && strcmp("123456", password) == 0)
    {
        // 认证成功
        sdata->flag = flag | PASSWORD_PASS;
        return SSH_AUTH_SUCCESS;
    }
    else
    {
        // 错误次数
        sdata->flag = (flag | PASSWORD_ERROR_NUMBER) == PASSWORD_ERROR_NUMBER ? PASSWORD_ERROR_NUMBER : (flag | PASSWORD_ERROR_NUMBER)+1;
        return SSH_AUTH_DENIED;
    }
}

// 空认证方法 
int SSHServer::auth_none_callback(ssh_session session, const char *user, void *userdata){
    return SSH_AUTH_DENIED;
}

int SSHServer::auth_pubkey(ssh_session session, const char *user, struct ssh_key_struct *pubkey,
                char signature_state, void *userdata)
{
    struct SSHFort *sdata = (SSHFort *)userdata;
    if (signature_state == SSH_PUBLICKEY_STATE_NONE)
    {
        return SSH_AUTH_SUCCESS;
    }
    if (signature_state != SSH_PUBLICKEY_STATE_VALID)
    {
        return SSH_AUTH_DENIED;
    }
    const char *authorizedkeys = "./authen_key";
    if (authorizedkeys[0])
    {
        ssh_key key = NULL;
        int result;
        struct stat buf;
        if (stat(authorizedkeys, &buf) == 0)
        {
            result = ssh_pki_import_pubkey_file(authorizedkeys, &key);
            if ((result != SSH_OK) || (key == NULL))
            {
                fprintf(stderr,
                        "Unable to import public key file %s\n",
                        authorizedkeys);
            }
            else
            {
                result = ssh_key_cmp(key, pubkey, SSH_KEY_CMP_PUBLIC);
                ssh_key_free(key);
                if (result == 0)
                {
                    sdata->flag =  sdata->flag &  PASSWORD_PASS;
                    return SSH_AUTH_SUCCESS;
                }
            }
        }
    }
    return SSH_AUTH_DENIED;
}

int  SSHServer::request_callback(ssh_session session, const char *service, void *userdata){
    return SSH_OK;
}

ssh_channel SSHServer::open_channel(ssh_session session, void *userdata)
{
    SSHFort *sdata = (SSHFort *)userdata;
    sdata->client = new SSHClient(session);
    ssh_channel channel = sdata->client->get_channel();
    if (channel == NULL)
    {
        sdata->flag = sdata->flag | CHANNEL_ERROR;
        return channel;
    }

    sdata->asset = new SSHClient("localhost", "22", "gtinlian", "123456");
    if(!sdata->asset->status()){
        sdata->flag = sdata->flag | CHANNEL_ERROR;
        return channel;
    }

    if(sdata->client->status()){
        struct ssh_channel_callbacks_struct* channel_cb = new ssh_channel_callbacks_struct;
        channel_cb->userdata = sdata;
        channel_cb->channel_data_function = data_function;
        channel_cb->channel_pty_request_function = pty_request;
        channel_cb->channel_shell_request_function = shell_request;
        channel_cb->channel_pty_window_change_function = pty_resize;
        channel_cb->channel_exec_request_function = exec_request;
        channel_cb->channel_env_request_function= env_request_function;
        channel_cb->channel_subsystem_request_function = subsystem_request;
        ssh_callbacks_init(channel_cb);
        sdata->channel_callbacks = channel_cb;
        ssh_set_channel_callbacks(sdata->client->channel, channel_cb);
        if(!sdata->client_do_epoll()) {
            delete sdata;
            return NULL;
        }
    }
    return channel;
}

// 客户端通讯相关的回调
int SSHServer::pty_request(ssh_session session, ssh_channel channel,
                const char *term, int cols, int rows, int py, int px,
                void *userdata)
{
    SSHFort *sdata = (SSHFort *)userdata;
    return ssh_channel_request_pty_size(sdata->asset->channel, term, cols, rows);        
}

int SSHServer::pty_resize(ssh_session session, ssh_channel channel, int cols,
               int rows, int py, int px, void *userdata)
{
    SSHFort *sdata = ( SSHFort *)userdata;
    return ssh_channel_change_pty_size(sdata->asset->channel, cols, rows);
}

int SSHServer::shell_request(ssh_session session, ssh_channel channel,
                  void *userdata)
{
    SSHFort *sdata = (SSHFort *)userdata;
    if(ssh_channel_request_shell(sdata->asset->channel) != SSH_OK 
    || ssh_event_add_session(sdata->poll_event, sdata->asset->session) != SSH_OK
    || ssh_set_channel_callbacks(sdata->asset->channel, sdata->channel_callbacks) != SSH_OK
    || ssh_event_add_session(sdata->poll_event, sdata->asset->session) != SSH_OK
    || !sdata->asset_do_epoll()){
        free(sdata);
        sdata->flag  = sdata->flag | ASSET_ERROR;
        return SSH_ERROR;
    }
    return SSH_OK;
}

int SSHServer::exec_request(ssh_session session, ssh_channel channel,
                 const char *command, void *userdata)
{
    SSHFort *sdata = (SSHFort  *)userdata;
    return ssh_channel_request_exec(sdata->asset->channel, command);
}

int SSHServer::subsystem_request(ssh_session session, ssh_channel channel,
                      const char *subsystem, void *userdata)
{
    SSHFort *sdata = (SSHFort *)userdata;
    if (strcmp(subsystem, "sftp") == 0)
    {
        if (ssh_channel_request_subsystem(sdata->asset->channel, subsystem) != SSH_OK 
            || ssh_event_add_session(sdata->poll_event, sdata->asset->session) != SSH_OK 
            || ssh_set_channel_callbacks(sdata->asset->channel, sdata->channel_callbacks) != SSH_OK 
            || ssh_event_add_session(sdata->poll_event, sdata->asset->session) != SSH_OK 
            || !sdata->asset_do_epoll())
        {
            sdata->flag  = sdata->flag | ASSET_ERROR;
            return SSH_FATAL;
        }
    }
    return  SSH_OK;
}

int SSHServer::env_request_function(ssh_session session, ssh_channel channel, const char *env_name,
                         const char *env_value, void *userdata)
{
     SSHFort *sdata = (SSHFort *)userdata;
    return ssh_channel_request_env(sdata->asset->channel, env_name, env_value);
}

int SSHServer::data_function(ssh_session session, ssh_channel channel, void *data,
                  uint32_t len, int is_stderr, void *userdata)
{
    SSHFort *sdata = (SSHFort *)userdata;
    if(sdata->client->eq_channel(channel)){
        return ssh_channel_write(sdata->asset->channel, data, len);
    }
    write(1, data, len);
    return ssh_channel_write(sdata->client->channel, data, len);
}
